home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / intrvews / xgrab.lha / xgrab / ui / stringedit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-25  |  11.0 KB  |  528 lines

  1. /**
  2.    GRAB Graph Layout and Browser System
  3.  
  4.    Copyright (c) 1987, 1988, 1989 Stanford University
  5.    Copyright (c) 1989, Tera Computer Company
  6.  **/
  7.  
  8.   /**
  9.      stolen from the InterViews 2.5 source files (text directory, which 
  10.      is unsupported in 2.6.  
  11.    **/
  12.  
  13. /*
  14.  * StringEdit - basic interactive editor for character strings
  15.  */
  16.  
  17. #include "stringedit.h"
  18. #include <InterViews/sensor.h>
  19. #include <InterViews/paint.h>
  20. #include <InterViews/cursor.h>
  21. #include <InterViews/shape.h>
  22. #include <InterViews/painter.h>
  23. #include <InterViews/scene.h>
  24. #include <string.h>
  25. #include <ctype.h>
  26.  
  27. static const char SEAccept = '\015';        // ^M, CR
  28. static const char SECancel = '\007';        // ^G
  29. static const char SEDeleteBefore = '\177';    // ^?
  30. static const char SEDeleteAfter = '\004';    // ^D
  31. static const char SEErase = '\010';        // ^H
  32. static const char SERestart = '\022';        // ^R
  33. static const char SELeft = '\002';        // ^B
  34. static const char SERight = '\006';        // ^F
  35. static const char SEBegin = '\001';        // ^A
  36. static const char SEEnd = '\005';        // ^E
  37. static const char SESelectAll = '\025';        // ^U
  38. static const char SESelectWord = '\027';    // ^W
  39. static const char SECut = '\030';        // ^X
  40. static const char SEPaste = '\026';        // ^V
  41. static const char SECopy = '\003';        // ^C
  42.  
  43. static const int SEDoubleClickTime = 200;    // ms
  44. static const int SEDoubleClickOffset = 3;    // pixels
  45.  
  46. inline int abs (int x) { return (x>=0) ? x : -x; }
  47.  
  48. StringEdit::StringEdit (const char* sample, int b) {
  49.     Init(sample, b);
  50. }
  51.  
  52. StringEdit::StringEdit (const char* name, const char* sample, int b) {
  53.     SetInstance(name);
  54.     Init(sample, b);
  55. }
  56.  
  57. StringEdit::StringEdit (const char* sample, Painter* p, int b) : (nil, p) {
  58.     Init(sample, b);
  59. }
  60.  
  61. void StringEdit::Init (const char* s, int b) {
  62.     SetClassName("StringEdit");
  63.     selecting = false;
  64.     caret = false;
  65.     border = b;
  66.     sample = s;
  67.     old = sample;
  68.     buffer = new char[SEBufferSize];
  69.     SetCursor(ltextCursor);
  70.     input = new Sensor;
  71.     input->Catch(DownEvent);
  72.     edit = new Sensor;
  73.     edit->Catch(KeyEvent);
  74.     edit->Catch(DownEvent);
  75.     edit->Catch(MotionEvent);
  76.     select = new Sensor;
  77.     select->Catch(MotionEvent);
  78.     select->Catch(UpEvent);
  79.     highlight = nil;
  80.     lasttime = 0;
  81.     lastx = lasty = 0;
  82.     Restart();
  83.     Flash(5);
  84.     Extra(0);
  85. }
  86.  
  87. void StringEdit::Reconfig () {
  88.     delete highlight;
  89.     highlight = new Painter(output);
  90.     highlight->SetColors(output->GetBgColor(), output->GetFgColor());
  91.     shape->hunits = output->GetFont()->Width("n");
  92.     shape->vunits = output->GetFont()->Height();
  93.     int height = shape->vunits + 2 * border;
  94.     int width = output->GetFont()->Width(sample) + 2 * border;
  95.     shape->Rect(width, height);
  96.     shape->Rigid(0, hfil, 0, 0);
  97. }
  98.  
  99. StringEdit::~StringEdit () {
  100.     delete edit;
  101.     delete select;
  102.     delete highlight;
  103.     delete buffer;
  104. }
  105.  
  106. boolean StringEdit::DoubleClick (Event& e) {
  107.     return (
  108.     e.eventType == DownEvent
  109.     && (e.timestamp - lasttime) < SEDoubleClickTime
  110.     && (abs(e.x-lastx) + abs(e.y-lasty)) < SEDoubleClickOffset
  111.     );
  112. }
  113.  
  114. void StringEdit::HideSelection () {
  115.     if (EmptySelection()) {
  116.     HideCaret();
  117.     } else {
  118.     Refresh(prev+1, next-1, output);
  119.     }
  120. }
  121.  
  122. void StringEdit::ShowSelection () {
  123.     if (EmptySelection()) {
  124.     ShowCaret();
  125.     } else {
  126.     Refresh(prev+1, next-1, highlight);
  127.     }
  128. }
  129.  
  130. void StringEdit::HideCaret () {
  131.     if (caret && EmptySelection()) {
  132.     Refresh(prev, next, output);
  133.     }
  134.     caret = false;
  135. }
  136.  
  137. void StringEdit::ShowCaret () {
  138.     if (!caret && EmptySelection()) {
  139.     DrawCaret(next);
  140.     }
  141.     caret = true;
  142. }
  143.  
  144. void StringEdit::Restart () {
  145.     length = min(strlen(old), SEBufferSize-1);
  146.     strncpy(buffer, old, length);
  147.     Unselect();
  148. }
  149.  
  150. void StringEdit::DoChar (char c) {
  151.     switch (c) {
  152.     case SERestart :
  153.     Restart(); Draw(); break;
  154.     case SELeft :
  155.     HideSelection();
  156.     prev = max(-1, prev - 1);
  157.     next = prev + 1;
  158.     ShowSelection();
  159.     break;
  160.     case SERight :
  161.     HideSelection();
  162.     next = min(length, next + 1);
  163.     prev = next - 1;
  164.     ShowSelection();
  165.     break;
  166.     case SEBegin:
  167.     HideSelection();
  168.     prev = -1;
  169.     next = prev + 1;
  170.     ShowSelection();
  171.     break;
  172.     case SEEnd:
  173.     HideSelection();
  174.     next = length;
  175.     prev = next - 1;
  176.     ShowSelection();
  177.     break;
  178.     case SEErase :
  179.     case SEDeleteBefore:
  180.     if (! EmptySelection()) {
  181.         Clear();
  182.     } else if (! AtBegin()) {
  183.         DeleteChar(prev);
  184.     }
  185.     break;
  186.     case SEDeleteAfter:
  187.     if (! EmptySelection()) {
  188.         Clear();
  189.     } else if (! AtEnd()) {
  190.         DeleteChar(next);
  191.     }
  192.     break;
  193.     case SESelectAll :
  194.     Select(); ShowSelection(); break;
  195.     case SESelectWord :
  196.     SelectWord(); ShowSelection(); break;
  197.     case SECut :
  198.     Cut(); break;
  199.     case SEPaste:
  200.     Clear(); Paste(); break;
  201.     case SECopy:
  202.     Copy(); break;
  203.     default :
  204.     if (!iscntrl(c)) {
  205.         Clear();
  206.         AddChar(c);
  207.     }
  208.     break;
  209.     }
  210. }
  211.  
  212. int StringEdit::Hit (Coord x) {
  213.     return output->GetFont()->Index(buffer, length, x, true);
  214. }
  215.  
  216. void StringEdit::DeleteChar (int i) {
  217.     if (i < 0 || i >= length) {
  218.     return;
  219.     }
  220.     --length;
  221.     if (prev >= i) {
  222.     --prev;
  223.     }
  224.     if (next > i) {
  225.     --next;
  226.     }
  227.     for (int j = i; j<length; ++j) {
  228.     buffer[j] = buffer[j+1];
  229.     }
  230.     buffer[length] = ' ';
  231.     Refresh(i-1, length, output);
  232. }
  233.  
  234. void StringEdit::AddChar (char c) {
  235.     if (length >= SEBufferSize-1) {
  236.     return;
  237.     }
  238.     for (int i = length; i>next; --i) {
  239.     buffer[i] = buffer[i-1];
  240.     }
  241.     buffer[next] = c;
  242.     length += 1;
  243.     next += 1;
  244.     prev = next - 1;
  245.     Refresh(prev-1, length, output);
  246. }
  247.  
  248. void StringEdit::Clear () {
  249.     while (next > prev+1) {
  250.     DeleteChar(prev+1);
  251.     }
  252. }
  253.  
  254. void StringEdit::Cut () {
  255.     cliplength = 0;
  256.     while (prev < next-1) {
  257.     clip[ cliplength ] = buffer[ prev+1 ];
  258.     DeleteChar(prev+1);
  259.     cliplength += 1;
  260.     }
  261.     clip[ cliplength ] = '\0';
  262. }
  263.  
  264. void StringEdit::Copy () {
  265.     cliplength = 0;
  266.     for (int i = prev+1; i<next; ++i) {
  267.     clip[ cliplength ] = buffer[ i ];
  268.     cliplength += 1;
  269.     }
  270.     clip[ cliplength ] = '\0';
  271. }
  272.  
  273. void StringEdit::Paste () {
  274.     for (int i = 0; i<cliplength; ++i) {
  275.     AddChar(clip[i]);
  276.     }
  277. }
  278.  
  279. void StringEdit::StartSelection (Coord x) {
  280.     next = hitnext = Hit(x);
  281.     prev = hitprev = hitnext-1;
  282.     caret = true;
  283. }
  284.  
  285. void StringEdit::ExtendSelection (Coord x) {
  286.     int oldprev = prev;
  287.     int oldnext = next;
  288.     int h = Hit(x);
  289.     prev = min(h-1, hitprev);
  290.     next = max(h, hitnext);
  291.     if (next > oldnext) {
  292.     Refresh(oldnext, next-1, highlight);
  293.     } else if (next < oldnext) {
  294.     Refresh(next, oldnext-1, output);
  295.     }
  296.     if (prev < oldprev) {
  297.     Refresh(prev+1, oldprev, highlight);
  298.     } else if (prev > oldprev) {
  299.     Refresh(oldprev+1, prev, output);
  300.     }
  301. }
  302.  
  303. void StringEdit::SetString (const char* string, boolean select) {
  304.     if (string != nil) {
  305.     old = string;
  306.     Restart();
  307.     }
  308.     if (select) {
  309.     Select();
  310.     }
  311.     Draw();
  312. }
  313.  
  314. char* StringEdit::GetString () {
  315.     char* result = new char[length+1];
  316.     strncpy(result, buffer, length);
  317.     result[length] = '\0';
  318.     return result;
  319. }
  320.  
  321. void StringEdit::Handle (Event& e) {
  322.     e.target = this;
  323.     if (e.eventType != DownEvent) {
  324.     e.eventType = OnEvent;
  325.     }
  326.     Listen(edit);
  327.     boolean done = false;
  328.     while (!done) {
  329.     switch (e.eventType) {
  330.     case DownEvent:
  331.             SetCursor(ltextCursor);
  332.         if (e.target != this) {
  333.         UnRead(e);
  334.         done = true;
  335.         break;
  336.         }
  337.         if (selecting) {
  338.         break;
  339.         }
  340.         Listen(select);
  341.         selecting = true;
  342.         if (!e.shift) {
  343.         HideSelection();
  344.         }
  345.         if (DoubleClick(e)) {
  346.         if (e.shift) {
  347.             Select();
  348.         } else {
  349.             SelectWord();
  350.         }
  351.         } else {
  352.         if (e.shift) {
  353.             ExtendSelection(Coord(e.x-border));
  354.         } else {
  355.             StartSelection(Coord(e.x-border));
  356.         }
  357.         }
  358.         ShowSelection();
  359.         break;
  360.     case MotionEvent:
  361.             SetCursor(ltextCursor);
  362.         if (!selecting) {
  363.         break;
  364.         }
  365.         HideCaret();
  366.         if (e.target != this) {
  367.         e.target->GetRelative(e.x, e.y, this);
  368.         }
  369.         ExtendSelection(Coord(e.x-border));
  370.         break;
  371.     case UpEvent:
  372.         if (!selecting) {
  373.         break;
  374.         }
  375.         caret = false;
  376.         ShowCaret();
  377.         selecting = false;
  378.         Listen(edit);
  379.         break;
  380.     case KeyEvent:
  381.         SetCursor(noCursor);
  382.         if (e.len == 0) {
  383.         break;
  384.         }
  385.         if (e.keystring[0] == SEAccept || e.keystring[0] == SECancel) {
  386.         UnRead(e);
  387.         done = true;
  388.         break;
  389.         }
  390.         HideSelection();
  391.         DoChar(e.keystring[0]);
  392.         FixSize();
  393.         ShowSelection();
  394.         break;
  395.     case TimerEvent:
  396.         if (caret) {
  397.         HideCaret();
  398.         } else {
  399.         ShowCaret();
  400.         }
  401.         break;
  402.     default:
  403.         break;
  404.     }
  405.     if (e.eventType == UpEvent) {
  406.         lasttime = e.timestamp;
  407.         lastx = e.x;
  408.         lasty = e.y;
  409.     }
  410.     if (!done) {
  411.         Read(e);
  412.     }
  413.     }
  414.     SetCursor(ltextCursor);
  415.     Listen(input);
  416.     HideSelection();
  417.     Unselect();
  418. }
  419.  
  420. void StringEdit::Refresh (int from, int to, Painter* p) {
  421.     Font* font = output->GetFont();
  422.     int last = font->Index(buffer, length, xmax - 2*border, false);
  423.     int final = min(last, length-1);
  424.     if (from > to || from > last) {
  425.     return;
  426.     } else {
  427.     from = max(from, 0);
  428.     to = min(to, final);
  429.     }
  430.     Coord left = font->Width(buffer, from) + border;
  431.     Coord base = (ymax+1 - shape->vunits)/2;
  432.     if (from == 0) {
  433.     output->ClearRect(canvas, 0, 0, left-1, ymax);
  434.     }
  435.     p->MoveTo(left, base);
  436.     p->Text(canvas, buffer+from, to+1-from);
  437.     if (to == final) {
  438.     Coord x, y;
  439.     p->GetPosition(x, y);
  440.     output->ClearRect(canvas, x, 0, xmax, ymax);
  441.     if (length-1 > last) {
  442.         output->FillRect(canvas, x+1, y-3, x+2, y+shape->hunits-4);
  443.         output->FillRect(canvas, x-shape->hunits+3, y-3, x, y-2);
  444.     } else if (length-1 == last) {
  445.         output->ClearRect(canvas, x-shape->hunits+3, y-3, x, y-2);
  446.     }
  447.     }
  448. }
  449.  
  450. void StringEdit::DrawCaret (int pos) {
  451.     Font* font = output->GetFont();
  452.     Coord base = (ymax+1 - shape->vunits)/2;
  453.     Coord caretx = font->Width(buffer, pos) + border;
  454.     output->Line(canvas, caretx, base, caretx, base+shape->vunits-1);
  455. }
  456.  
  457. void StringEdit::FixSize () {
  458.     if (extra == 0) {
  459.     return;
  460.     }
  461.     Font* font = output->GetFont();
  462.     int w = font->Width(buffer, length) + 2 * border;
  463.     if (w > xmax+1) {
  464.     shape->width = int(w + extra * shape->hunits);
  465.     Parent()->Change(this);
  466.     }
  467. }
  468.  
  469. void StringEdit::Unselect () {
  470.     prev = hitprev = length-1;
  471.     next = hitprev = length;
  472. }
  473.  
  474. void StringEdit::Select () {
  475.     prev = hitprev = -1;
  476.     next = hitnext = length;
  477. }
  478.  
  479. void StringEdit::SelectWord () {
  480.     while (next < length && isalnum(buffer[next])) {
  481.     ++next;
  482.     }
  483.     while (prev >= 0 && !isalnum(buffer[prev])) {
  484.     --prev;
  485.     }
  486.     while (prev >= 0 && isalnum(buffer[prev])) {
  487.     --prev;
  488.     }
  489.     hitprev = prev;
  490.     hitnext = next;
  491. }
  492.  
  493. void StringEdit::Flash (int flash) {
  494.     if (flash > 0) {
  495.     edit->CatchTimer(flash/10, (flash%10) * 100000);
  496.     } else {
  497.     edit->Ignore(TimerEvent);
  498.     }
  499. }
  500.  
  501. void StringEdit::Extra (int ex) {
  502.     extra = ex;
  503. }
  504.  
  505. void StringEdit::Redraw (Coord, Coord, Coord, Coord) {
  506.     if (canvas == nil) {
  507.     return;
  508.     }
  509.     output->ClearRect(canvas, 0, 0, xmax, ymax);
  510.     if (EmptySelection()) {
  511.     Refresh(0, length-1, output);
  512.     if (caret) {
  513.         DrawCaret(next);
  514.     }
  515.     } else {
  516.     Refresh(0, prev, output);
  517.     Refresh(prev+1, next-1, highlight);
  518.     Refresh(next, length-1, output);
  519.     }
  520. }
  521.  
  522. void StringEdit::Reshape (Shape& s) {
  523.     *shape = s;
  524.     if (Parent() != nil) {
  525.     Parent()->Change(this);
  526.     }
  527. }
  528.